package com.vhall.component.service.rbac.impl;


import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.vhall.component.dao.rbac.RbacActionsMapper;
import com.vhall.component.dao.rbac.RbacRoleActionsMapper;
import com.vhall.component.dao.rbac.RbacRoleMapper;
import com.vhall.component.entity.account.ActionsEntity;
import com.vhall.component.entity.account.RoleActionsEntity;
import com.vhall.component.entity.account.RoleEntity;
import com.vhall.component.entity.rbac.vo.*;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.service.rbac.RbacActionService;
import com.vhall.component.service.rbac.RbacInitialization;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author dalong
 * @date 2021-05-13 19:36
 */
@Service
@Slf4j
public class RbacActionServiceImpl implements RbacActionService {

    private static final List<Integer> INIT_ROLE_ACTION_LIST = Arrays.asList(10, 11, 12, 32, 45, 46, 48, 57);

    @Autowired
    private RbacActionsMapper actionsMapper;

    @Autowired
    private RbacRoleMapper rbacRoleMapper;

    @Autowired
    private RbacRoleActionsMapper rbacRoleActionsMapper;


    @Autowired
    private RbacInitialization rbacInitialization;

    @Override
    public ActionsVO add(ActionAddReqVO reqVO) {
        ActionsEntity actionsEntity = getInfo(reqVO.getControllerName(), reqVO.getActionName());
        if (actionsEntity != null) {
            throw new BusinessException(BizErrorCode.AUTH_ACCESS_EXIST);
        }
        if (reqVO.getPid() != null && reqVO.getPid() != 0) {
            if (StringUtils.isEmpty(reqVO.getActionName())) {
                throw new BusinessException(BizErrorCode.BIZ_ACTION_NAME_NOT_NULL);
            }
            ActionsEntity pAction = actionsMapper.selectById(reqVO.getPid());
            if (Objects.nonNull(pAction)) {
                reqVO.setControllerName(pAction.getControllerName());
            }
        }
        // create
        actionsEntity = new ActionsEntity();
        actionsEntity.setControllerName(reqVO.getControllerName());
        actionsEntity.setActionName(reqVO.getActionName());
        actionsEntity.setActionTag(reqVO.getActionTag());
        actionsEntity.setDesc(reqVO.getDesc());
        actionsEntity.setType(reqVO.getType());
        actionsEntity.setPid(reqVO.getPid() == null ? 0 : reqVO.getPid());
        actionsEntity.setUpdatedAt(LocalDateTime.now());
        actionsEntity.setCreatedAt(LocalDateTime.now());
        actionsMapper.insert(actionsEntity);
        actionsEntity.setActionId(actionsEntity.getActionId());

        ActionsVO actionsVO = new ActionsVO();
        BeanUtils.copyProperties(actionsEntity, actionsVO);
        return actionsVO;
    }

    @Override
    public boolean batchDelete(ActionDeleteReqVO reqVO) {

        String[] idList = reqVO.getActionIds().split(",");
        if (idList.length > 0) {
            LambdaUpdateWrapper<ActionsEntity> updateWrapper = Wrappers.lambdaUpdate();
            updateWrapper.in(ActionsEntity::getActionId, Arrays.asList(idList))
                    .set(ActionsEntity::getDeletedAt, LocalDateTime.now())
                    .set(ActionsEntity::getStatus, -1);
            actionsMapper.update(null, updateWrapper);
        }
        return true;
    }

    @Override
    public boolean update(ActionEditReqVO reqVO) {
        ActionsEntity actionsEntity = getInfoById(reqVO.getActionId());
        if (actionsEntity == null) {
            throw new BusinessException(BizErrorCode.AUTH_ACCESS_EMPTY);
        }
        if (reqVO.getPid() > 0) {
            ActionsEntity parentActionsEntity = getInfoById(reqVO.getPid().toString());
            if (parentActionsEntity == null) {
                throw new BusinessException(BizErrorCode.AUTH_ACCESS_PARENT_EMPTY);
            }
            if (StringUtils.isEmpty(reqVO.getActionName())) {
                reqVO.setActionName(actionsEntity.getActionName());
            }
        }
        LambdaQueryWrapper<ActionsEntity> where = Wrappers.lambdaQuery();
        where.eq(ActionsEntity::getControllerName, reqVO.getControllerName());
        if (StringUtils.isEmpty(reqVO.getActionName())) {
            where.eq(ActionsEntity::getActionName, reqVO.getActionName());
        }
        where.isNull(ActionsEntity::getDeletedAt).last("LIMIT 1");
        actionsEntity = actionsMapper.selectOne(where);
        if (actionsEntity != null && !actionsEntity.getActionId().equals(Integer.valueOf(reqVO.getActionId()))) {
            throw new BusinessException(BizErrorCode.AUTH_ACCESS_EXIST);
        }
        // update
        actionsEntity = new ActionsEntity();
        BeanUtils.copyProperties(reqVO, actionsEntity);
        actionsEntity.setActionId(Integer.valueOf(reqVO.getActionId()));
        actionsEntity.setUpdatedAt(LocalDateTime.now());
        return actionsMapper.updateById(actionsEntity) > 0;
    }

    @Override
    public boolean updateStatus(ActionEditReqVO reqVO) {
        ActionsEntity actionsEntity = getInfoById(reqVO.getActionId());
        if (actionsEntity == null) {
            throw new BusinessException(BizErrorCode.AUTH_ACCESS_EMPTY);
        }
        if (reqVO.getStatus() == null) {
            throw new BusinessException(BizErrorCode.BIZ_PARAM_NOT_NULL);
        } else {
            actionsEntity.setStatus(reqVO.getStatus());
            if (reqVO.getStatus() == -1) {
                actionsEntity.setDeletedAt(LocalDateTime.now());
            }
        }
        actionsEntity.setUpdatedAt(LocalDateTime.now());
        return actionsMapper.updateById(actionsEntity) > 0;
    }

    @Override
    public ActionListRspVO getList(ActionListReqVO reqVO) {
        ActionListRspVO rspVO = new ActionListRspVO();
        LambdaQueryWrapper<ActionsEntity> where = Wrappers.lambdaQuery();
        where.isNull(ActionsEntity::getDeletedAt);
        where.orderByDesc(ActionsEntity::getActionId);
        if (reqVO.getStatus() != null) {
            where.eq(ActionsEntity::getStatus, reqVO.getStatus());
        }
        int currPage = 1;
        int pageSize = 1000;
        int offset = (currPage - 1) * pageSize;
        where.last("limit " + offset + "," + pageSize);
        List<ActionsEntity> actionsListByPage = actionsMapper.selectList(where);
        Integer[] integerList = {10, 11, 12, 32, 45, 46, 48, 57};
        List<Integer> initRoleActionList = Arrays.stream(integerList).collect(Collectors.toList());
        List<ActionsRspVO> actionsRspVos = new LinkedList<>();
        for (ActionsEntity actionsEntity : actionsListByPage) {
            ActionsRspVO actionsRspVO = new ActionsRspVO();
            BeanUtils.copyProperties(actionsEntity, actionsRspVO);
            actionsRspVO.setDisabled(actionsEntity.getStatus() == 2);
            actionsRspVO.setDisabled(initRoleActionList.contains(actionsEntity.getActionId()));
            if (actionsEntity.getType() == 1) {
                actionsRspVO.setTypeName("admin操作");
            } else {
                actionsRspVO.setTypeName("控制台操作");
            }
            actionsRspVos.add(actionsRspVO);
        }
        if (reqVO.getTree() == 0) {
            int total = actionsRspVos.size();
            rspVO.setCurrentPage(currPage);
            rspVO.setPerPage(pageSize);
            rspVO.setTotal(total);
            rspVO.setPath("/");
            Integer lastPage = total % pageSize == 0 ? total / pageSize : (total / pageSize) + 1;
            rspVO.setLastPage(lastPage);
            int firstItem = offset + 1;
            rspVO.setFrom(firstItem);
            Integer lastItem = firstItem + total - 1;
            rspVO.setTo(lastItem);
            rspVO.setFirstPageUrl(getUrl(1));
            rspVO.setLastPageUrl(getUrl(lastPage));
            if (lastPage > currPage) {
                rspVO.setLastPageUrl(getUrl(currPage + 1));
            }
            rspVO.setPrevPageUrl(getUrl(currPage - 1));
            rspVO.setData(actionsRspVos);
            return rspVO;
        }
        List<ActionsRspVO> result = new LinkedList<>();
        for (ActionsRspVO actionsRspVO : actionsRspVos) {
            if (actionsRspVO.getPid().equals(0)) {
                List<ActionsRspVO> treeList = getTreeList(actionsRspVos, actionsRspVO.getActionId());
                actionsRspVO.setChildren(treeList);
                if (actionsRspVO.getType() == 1) {
                    actionsRspVO.setTypeName("admin操作");
                } else {
                    actionsRspVO.setTypeName("控制台操作");
                }
                actionsRspVO.setDisabled(actionsRspVO.getStatus() == 2);
                result.add(actionsRspVO);
            }

        }
        int total = result.size();
        rspVO.setCurrentPage(currPage);
        rspVO.setPerPage(pageSize);
        rspVO.setTotal(total);
        rspVO.setPath("/");
        Integer lastPage = total % pageSize == 0 ? total / pageSize : (total / pageSize) + 1;
        rspVO.setLastPage(lastPage);
        int firstItem = offset + 1;
        rspVO.setFrom(firstItem);
        Integer lastItem = firstItem + total - 1;
        rspVO.setTo(lastItem);
        rspVO.setFirstPageUrl(getUrl(1));
        rspVO.setLastPageUrl(getUrl(lastPage));
        if (lastPage > currPage) {
            rspVO.setLastPageUrl(getUrl(currPage + 1));
        }
        if (currPage > 1) {
            rspVO.setPrevPageUrl(getUrl(currPage - 1));
        }
        rspVO.setData(result);
        return rspVO;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean edit(EditActionReqVO req) {
        RoleEntity roleEntity = rbacRoleMapper.selectById(req.getRoleId());
        if (roleEntity == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROLE);
        }
        List<Integer> actionList = StrUtil.splitTrim(req.getActionIds(), ",")
                .stream()
                .map(Integer::valueOf)
                .collect(Collectors.toList());
        actionList.addAll(INIT_ROLE_ACTION_LIST);
        Boolean refresh = refresh(req.getRoleId(), actionList);

        // 刷新缓存
        rbacInitialization.doInit(req.getRoleId());
        if (!refresh) {
            throw new BusinessException(BizErrorCode.BIZ_EDIT_FAILED);
        }
        return true;

    }

    private Boolean refresh(Integer roleId, List<Integer> actionList) {
        RoleEntity roleEntity = rbacRoleMapper.selectById(roleId);
        try {
            if (roleEntity != null) {
                LambdaQueryWrapper<RoleActionsEntity> where = Wrappers.lambdaQuery();
                where.eq(RoleActionsEntity::getRoleId, roleId);
                rbacRoleActionsMapper.delete(where);
            }
            List<Integer> distinctActionList = actionList.stream().distinct().collect(Collectors.toList());
            List<RoleActionsEntity> roleActionsEntityList = new ArrayList<>(distinctActionList.size());
            for (Integer actionId : distinctActionList) {
                ActionsEntity actionInfo = actionsMapper.selectById(actionId);
                if (actionInfo != null) {
                    RoleActionsEntity rae = new RoleActionsEntity();
                    rae.setRoleId(roleId);
                    rae.setActionId(actionId);
                    rae.setCreatedAt(LocalDateTime.now());
                    rae.setUpdatedAt(LocalDateTime.now());
                    roleActionsEntityList.add(rae);
                    // 这里可以换成批量插入
//                    rbacRoleActionsMapper.insert(rae);
                }
            }
            rbacRoleActionsMapper.insertBatch(roleActionsEntityList);
            return true;
        } catch (Exception e) {
            log.error("编辑失败,{}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取权限信息
     *
     * @param controllerName
     * @param actionName
     * @return
     */
    private ActionsEntity getInfo(String controllerName, String actionName) {
        LambdaQueryWrapper<ActionsEntity> where = Wrappers.lambdaQuery();
        where.eq(ActionsEntity::getControllerName, controllerName)
                .eq(ActionsEntity::getActionName, actionName)
                .isNull(ActionsEntity::getDeletedAt)
                .last("LIMIT 1");
        return actionsMapper.selectOne(where);
    }

    /**
     * 根据action_id查询数据
     *
     * @param id
     * @return
     */
    private ActionsEntity getInfoById(String id) {
        LambdaQueryWrapper<ActionsEntity> where = Wrappers.lambdaQuery();
        where.eq(ActionsEntity::getActionId, id)
                .isNull(ActionsEntity::getDeletedAt)
                .last("LIMIT 1");
        return actionsMapper.selectOne(where);
    }

    /**
     * 获取每个子模块
     *
     * @param
     * @return
     */
    private List<ActionsRspVO> getTreeList(List<ActionsRspVO> lists, Integer pid) {
        List<ActionsRspVO> result = new ArrayList<>();
        lists.forEach(actionsRspVO -> {
            if (pid.equals(actionsRspVO.getPid())) {
                actionsRspVO.setDisabled(actionsRspVO.getStatus() == 2);
                result.add(actionsRspVO);
                List<ActionsRspVO> children = getTreeList(lists, actionsRspVO.getActionId());
                actionsRspVO.setChildren(children);
            }
        });
        return result;
    }


    private String getUrl(Integer page) {
        if (page <= 0) {
            page = 1;
        }
        return "/?page=" + page;
    }
}
